//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Object properties dialog - 'Elements' page.
//

# include "StdAfx.h"
# include "Resource.h"
# include "RgnProperties.h"
# include "Map.h"
# include "MapView.h"
# include "CombinedAction.h"
# include "DeleteElement.h"
# include "CopyElement.h"
# include "EditElement.h"
# include "SetRgnLevels.h"
# include "SelectLevels.h"
# include "RefreshHoles.h"
# include "Selection.h"
# include "Scale.h"
# include "Position.h"
# include "ListView.h"
# include "ParseHelpers.h"
# include "PrintUnits.h"
# include "Settings.h"
# include "fast_floor.h"
# include "Globals.h"

enum {
	lvOrdinal  = 0,
	lvLevel    = 1,
	lvPosition = 2,
	lvPoints   = 3,
	lvSize     = 4,
	lvHole     = 5
};

static int g_iSortColumn = -1;
static bool g_bAccent = true;

static
int CALLBACK _CompareItems (LPARAM _lParam1, LPARAM _lParam2, LPARAM _lParamSort) {
	const HWND hList = reinterpret_cast<HWND> (_lParamSort);

	char str1 [64], str2 [64];
	ListView_GetItemText (hList, _lParam1, g_iSortColumn, str1, 64); 
	ListView_GetItemText (hList, _lParam2, g_iSortColumn, str2, 64);

	if (g_iSortColumn == 0 || g_iSortColumn == 1 || g_iSortColumn == 3)
		// Compare as integers.
		return g_bAccent ? ParseDecU (str1, str1 + 64) - ParseDecU (str2, str2 + 64) : ParseDecU (str2, str2 + 64) - ParseDecU (str1, str1 + 64);
	else if (g_iSortColumn == 4)
		// Compare as floats.
		return g_bAccent ? ParseReal (str1, str1 + 64) - ParseReal (str2, str2 + 64) : ParseReal (str2, str2 + 64) - ParseReal (str1, str1 + 64);
	else
		// Compare as strings.
		return g_bAccent ? ::strcmp (str1, str2) : ::strcmp (str2, str1);
}

void CRgnElementsPage::_FillElements () {
	ListView_DeleteAllItems (m_hList);

	size_t cItem = 0;

	const rgn_origins_t & origins = m_rgn.origins;
	const size_t cOrigins = origins.size ();
	for (size_t cOrigin = 0; cOrigin < cOrigins; ++ cOrigin) {
		const rgn_origin_t & origin = origins [cOrigin];
		if (origin.bDeleted)
			continue;

		// #
		InsertItem (m_hList, cItem, cOrigin, "%d", cOrigin + 1);

		// Level.
		SetSubItemText (m_hList, cItem, lvLevel, "%d", origin.cLevel);

		// Position.
		char strPosition [64];
		PrintPosition (strPosition, origin.point);
		SetSubItemText (m_hList, cItem, lvPosition, "%s", strPosition);

		++ cItem;
	}

	const rgn_elements_t & elements = m_rgn.elements;
	const size_t cElements = elements.size ();
	for (size_t cElement = 0; cElement < cElements; ++ cElement) {
		const rgn_element_t & element = elements [cElement];
		if (element.bDeleted)
			continue;

		// #
		InsertItem (m_hList, cItem, cElement, "%d", cElement + 1);

		// Level.
		SetSubItemText (m_hList, cItem, lvLevel, "%d", element.cLevel);

		// Position.
		if (element.points.empty ())
			SetSubItemText (m_hList, cItem, lvPosition, "-");
		else {
			char strPosition [64];
			PrintPosition (strPosition, element.points [0]);
			SetSubItemText (m_hList, cItem, lvPosition, "%s", strPosition);
		}

		// Points.
		SetSubItemText (m_hList, cItem, lvPoints, "%d", element.points.size ());

		if (m_rgn.IsPolyline ()) {
			// Length.
			char strDistance [32];
			char strDistanceUnits [16];
			PrintDistance (strDistance, strDistanceUnits, element.fLengthOrArea*40e6/360);
			SetSubItemText (m_hList, cItem, lvSize, "%s %s", strDistance, strDistanceUnits);
		} else {
			assert (m_rgn.IsPolygon ());

			const float fCosY = element.rectBound.Empty () ? 1 : ::cos (element.rectBound.MeanY ()*c_PI/180);

			// Area.
			const float fArea = (element.fLengthOrArea < 1.e-11f ? GetArea (element.points) : element.fLengthOrArea)*40000000.f/360*40000000.f/360*fCosY;

			char strArea [32];
			char strAreaUnits [16];
			PrintArea (strArea, strAreaUnits, fArea);

			SetSubItemText (m_hList, cItem, lvSize, "%s %s", strArea, strAreaUnits);

			// Kind.
			SetSubItemText (m_hList, cItem, lvHole, element.bHole ? "Inner" : "Outer");
		}

		++ cItem;
	}

	// Set the best width.
	AutoSizeColumns (m_hList);
}

void CRgnElementsPage::_RefreshElementsBtns () {
	bool bMapHasLevels;
	{
		CCritSec::CLock lock (g_cs);
		bMapHasLevels = ! g_map.Levels.empty ();
	}

	const size_t cSelected = ListView_GetSelectedCount (m_hList);

	EnableDlgItem (IDC_REMOVE,        cSelected != 0);
	EnableDlgItem (IDC_COPY,          cSelected != 0 && bMapHasLevels);
	EnableDlgItem (IDC_EDIT,          cSelected == 1);
	EnableDlgItem (IDC_LOCATE,        cSelected == 1);
	EnableDlgItem (IDC_REFRESH_HOLES, m_rgn.IsPolygon ());
}

void CRgnElementsPage::_LocateElement (size_t _cElementOrOrigin) {
	UnselectObjects ();

	rect_t rect;
	size_t cLevel = 0;
	bool bFound = false;
	{
		CCritSec::CLock lock (g_cs);

		layer_t & layer = g_map.layers [m_rgn.Kind];
		rgns_t & rgns = layer.rgns;
		const size_t cRgns = layer.cRgns;

		for (rgns_t::iterator iRgn = rgns.begin (); iRgn != rgns.end (); ++ iRgn) {
			const rgn_t & rgn = * iRgn;
			if (& rgn != & m_rgn)
				continue;

			find_info_t fi;
			fi.pLayer           = & layer;
			fi.iRgn             = iRgn;
			fi.cElementOrOrigin = _cElementOrOrigin;
			g_Selection.push_back (fi);

			if (m_rgn.Kind == rkPoint) {
				if (_cElementOrOrigin < rgn.origins.size ()) {
					cLevel = rgn.origins [_cElementOrOrigin].cLevel;
					const point_t & pt = rgn.origins [_cElementOrOrigin].point;
					rect = rect_t (pt.x - 1e-3, pt.y - 1e-3, pt.x + 1e-3, pt.y + 1e-3);
					bFound = true;
				}
			} else {
				if (_cElementOrOrigin < rgn.elements.size ()) {
					cLevel = rgn.elements [_cElementOrOrigin].cLevel;
					rect = rgn.elements [_cElementOrOrigin].rectBound;
					bFound = true;
				}
			}
			
			break;
		}
	}

	if (bFound) {
		SetVisibleLevel (cLevel);
		SetScale (GetRectScale (rect));
		SetToCenter (point_t (rect.MeanX (), rect.MeanY ()));
		Refresh ();
	}
}

bool CRgnElementsPage::OnInit (HWND _hwndFocus) {
	//
	// List of elements.
	//

	m_hList = GetDlgItem (IDC_ELEMENTS);

	ListView_SetExtendedListViewStyleEx (m_hList, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);

	AddColumn (m_hList, lvOrdinal,      "#");
	AddColumn (m_hList, lvLevel,        "Level");
	if (m_rgn.IsPolyline () || m_rgn.IsPolygon ()) {
		AddColumn (m_hList, lvPosition, "Lat/Lon of 1st point");
		AddColumn (m_hList, lvPoints,   "Nodes", true);
		if (m_rgn.IsPolyline ()) {
			AddColumn (m_hList, lvSize, "Length", true);
			AddColumn (m_hList, lvHole, "");
		} else {
			AddColumn (m_hList, lvSize, "Area",   true);
			AddColumn (m_hList, lvHole, "Kind");
		}
	} else
		AddColumn (m_hList, lvPosition, "Lat/Lon");

	_FillElements ();

	SetSelection (m_hList, 0);
	_RefreshElementsBtns ();

	//
	// 'Levels' field.
	//

	SetDlgItemTextF (IDC_LEVELS, "%d", m_rgn.uchEndLevel);

	return true;
}

void CRgnElementsPage::OnCommand (WORD _wCmd, WORD _wNotifyCode, HWND _hwndCtl) {
	switch (_wCmd) {
		case IDC_LEVELS:
			if (_wNotifyCode == EN_CHANGE)
				PropSheetChanged ();
			break;

		case IDC_REMOVE: {
			const int iDepth = BeginCombinedAction ();

			size_t cItem = -1;
			while (true) {
				// Enumerate all selected items.
				cItem = ListView_GetNextItem (m_hList, cItem, LVNI_SELECTED);
				if (cItem == -1)
					break;

				DeleteElement (& m_rgn, GetLParam (m_hList, cItem));
			}

			EndCombinedAction (iDepth, GetStr (IDS_DELETE_MULTIPLE));

			_FillElements ();
			_RefreshElementsBtns ();

			Refresh ();

			break;
		}

		case IDC_COPY: {
			// Ask levels to copy.
			levels_t NewLevels;
			if (! SelectLevels (m_hWnd, NewLevels))
				break;
			if (NewLevels.empty ())
				break;

			const int iDepth = BeginCombinedAction ();

			layer_t & layer = g_map.layers [m_rgn.Kind];
			size_t cItem = -1;
			while (true) {
				// Enumerate all selected items.
				cItem = ListView_GetNextItem (m_hList, cItem, LVNI_SELECTED);
				if (cItem == -1)
					break;

				const size_t cElement = GetLParam (m_hList, cItem);
				for (levels_t::const_iterator il = NewLevels.begin (); il != NewLevels.end (); ++ il) {
					const size_t cNewLevel = * il;

					// Validate the new level.
					if (m_rgn.IsPolygon () || m_rgn.IsPolyline ()) {
						if (cNewLevel == m_rgn.elements [cElement].cLevel)
							continue;
					} else {
						if (cNewLevel == m_rgn.origins [cElement].cLevel)
							continue;
					}
					if (cNewLevel >= g_map.Levels.size () - 1)
						continue;

					CopyElement (cNewLevel, & m_rgn, cElement);
				}
			}

			EndCombinedAction (iDepth, GetStr (IDS_COPY_MULTIPLE_ELEMENTS));

			_FillElements ();
			_RefreshElementsBtns ();

			Refresh ();

			break;
		}

		case IDC_EDIT: {
			const size_t cSel = ListView_GetSelectionMark (m_hList);
			if (cSel == -1)
				break;

			const size_t cElementOrOrigin = GetLParam (m_hList, cSel);
			if (! EditElementPoints (m_hWnd, & m_rgn, cElementOrOrigin))
				break;

			_FillElements ();
			_RefreshElementsBtns ();

			Refresh ();

			break;
		}

		case IDC_LOCATE: {
			const size_t cSel = ListView_GetSelectionMark (m_hList);
			if (cSel == -1)
				break;

			const size_t cElementOrOrigin = GetLParam (m_hList, cSel);
			_LocateElement (cElementOrOrigin);
			break;
		}

		case IDC_REFRESH_HOLES: {
			RefreshHoles (& m_rgn);

			_FillElements ();
			_RefreshElementsBtns ();

			Refresh ();
			break;
		}
	}
}

bool CRgnElementsPage::OnNotify (int _idCtrl, NMHDR * _pInfo) {
	switch (_pInfo->code) {
		case LVN_COLUMNCLICK: {
			const NMLISTVIEW * const pInfo2 = reinterpret_cast<const NMLISTVIEW *> (_pInfo);

			if (g_iSortColumn != pInfo2->iSubItem)
				g_iSortColumn = pInfo2->iSubItem;
			else
				g_bAccent = ! g_bAccent;

			ListView_SortItemsEx (_pInfo->hwndFrom, _CompareItems, _pInfo->hwndFrom);

			break;
		}

		case LVN_ITEMCHANGED: {
			_RefreshElementsBtns ();
			break;
		}
	}

	return CPage::OnNotify (_idCtrl, _pInfo);
}

void CRgnElementsPage::OnApply (const PSHNOTIFY *) {
	const size_t cEndLevel = GetDlgItemUInt (IDC_LEVELS);
	if (cEndLevel != m_rgn.uchEndLevel) {
		SetObjectEndLevel (m_rgn, cEndLevel > 9 ? 9 : cEndLevel);
		Refresh ();
	}
}

